//
//  UIImage+Gif.m
//  boot
//
//  Created by Demi on 14/03/2017.
//  Copyright © 2017 Azazie. All rights reserved.
//

#import "UIImage+Gif.h"

#import "TXFire.h"
#import <ImageIO/ImageIO.h>
#import <ReactiveObjC/ReactiveObjC.h>

@implementation UIImage (Gif)

static inline CGImageSourceRef CreateImageSourceWithFile(NSString *path)
{
    CFDataRef data = (__bridge CFDataRef)[NSData dataWithContentsOfFile:path];
    if (data == NULL)
    {
        return NULL;
    }
    
    return CGImageSourceCreateWithData(data, nil);
}

static inline CFArrayRef CreateImagesWithSourceAndCount(CGImageSourceRef const source, size_t const count)
{
    CFMutableArrayRef results = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks);
    
    for (size_t i = 0; i < count; i++)
    {
        CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, nil);
        CFArrayAppendValue(results, image);
        CGImageRelease(image);
    }
    
    return results;
}

static inline float getDuration(CGImageSourceRef const source, size_t const count)
{
    float duration = 0.f;
    
    for (int i = 0; i < count; i++)
    {
        CFDictionaryRef const properties = CGImageSourceCopyPropertiesAtIndex(source, i, NULL);
        if (!properties)
        {
            continue;
        }
        
        CFDictionaryRef const gifProperties = CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary);
        if (gifProperties)
        {
            NSNumber *number =  (__bridge id)CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFUnclampedDelayTime);
            if (number == NULL || [number doubleValue] == 0)
            {
                number =  (__bridge id)CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFDelayTime);
            }
            if ([number doubleValue] > 0)
            {
                // Even though the GIF stores the delay as an integer number of centiseconds, ImageIO “helpfully” converts that to seconds for us.
                duration += number.floatValue;
            }
        }
        CFRelease(properties);
    }
    
    return duration;
}

+ (CFArrayRef)GIFImagesWithContentsOfFile:(NSString *)path duration:(out NSTimeInterval *)ti
{
    CGImageSourceRef source = CreateImageSourceWithFile(path);
    if (!source) return NULL;
    
    @onExit {
        CFRelease(source);
    };
    
    size_t count = CGImageSourceGetCount(source);
    if (count == 0)
    {
        return NULL;
    }
    
    CFArrayRef results = CreateImagesWithSourceAndCount(source, count);
    float duration = getDuration(source, count);
    if (ti)
    {
        *ti = duration;
    }
    return CFAutorelease(results);
}

+ (NSArray<UIImage *> *)imagesWithContentsOfFile:(NSString *)path duration:(out NSTimeInterval *)ti
{
    CFArrayRef const cgimages = [self GIFImagesWithContentsOfFile:path duration:ti];
    CFIndex const count = CFArrayGetCount(cgimages);
    NSMutableArray<UIImage *> *results = [NSMutableArray arrayWithCapacity:count];
    for (CFIndex idx = 0; idx < count; idx++)
    {
        [results addObject:[UIImage imageWithCGImage:(CGImageRef)CFArrayGetValueAtIndex(cgimages, idx)]];
    }
    return results;
}

+ (UIImage *)gifImageWithContentsOfFile:(NSString *)path
{
    NSData *data = [NSData dataWithContentsOfFile:path];
    if (!data) return nil;
    
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, nil);
    if (!source) return nil;
    
    @onExit {
        CFRelease(source);
    };
    
    size_t const count = CGImageSourceGetCount(source);
    if (count == 0) return nil;
    
    float duration = getDuration(source, count);
    
    NSMutableArray *images = [NSMutableArray arrayWithCapacity:count];
    for (size_t i = 0; i < count; i++)
    {
        CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, nil);
        [images addObject:[UIImage imageWithCGImage:image]];
        CGImageRelease(image);
    }
    
    return [UIImage animatedImageWithImages:images duration:duration];
}

+ (UIImage *)circleImageWithSize:(CGSize)size color:(UIColor *)color borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor {
    UIImage *oldImage = [UIImage tx_imageWithColor:color size:size ellipse:YES];
    
    CGFloat imageW = oldImage.size.width + 2 * borderWidth;
    CGFloat imageH = oldImage.size.height + 2 * borderWidth;
    CGSize imageSize = CGSizeMake(imageW, imageH);
    UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);
    
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    [borderColor set];
    CGFloat bigRadius = imageW * 0.5;
    CGFloat centerX = bigRadius;
    CGFloat centerY = bigRadius;
    CGContextAddArc(ctx, centerX, centerY, bigRadius, 0, M_PI * 2, 0);
    CGContextFillPath(ctx);
    
    CGFloat smallRadius = bigRadius - borderWidth;
    CGContextAddArc(ctx, centerX, centerY, smallRadius, 0, M_PI * 2, 0);
    CGContextClip(ctx);
    
    [oldImage drawInRect:CGRectMake(borderWidth, borderWidth, size.width, size.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return newImage;
}

+ (UIImage *)squareImageWithBorderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor oldImage:(UIImage *)inputImage{
    UIImage *oldImage = inputImage;
    CGFloat imageW = oldImage.size.width + 2 * borderWidth;
    CGFloat imageH = oldImage.size.height + 2 * borderWidth;
    CGSize imageSize = CGSizeMake(imageW, imageH);
    UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    [borderColor set];
    CGFloat bigRadius = imageW * 0.5;
    CGFloat centerX = bigRadius;
    CGFloat centerY = bigRadius;
    CGContextAddRect(ctx, CGRectMake(0, 0, imageW, imageH));
    CGContextFillPath(ctx);
    
    
    [oldImage drawInRect:CGRectMake(borderWidth, borderWidth, oldImage.size.width, oldImage.size.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return newImage;
}

@end
